1   /*
2    * Copyright (C) 2012 The Guava Authors
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  package com.google.common.util.concurrent.testing;
18  
19  import com.google.common.annotations.Beta;
20  import com.google.common.collect.ImmutableList;
21  import com.google.common.primitives.Longs;
22  import com.google.common.util.concurrent.AbstractFuture;
23  import com.google.common.util.concurrent.AbstractListeningExecutorService;
24  import com.google.common.util.concurrent.ListenableScheduledFuture;
25  import com.google.common.util.concurrent.ListeningScheduledExecutorService;
26  import com.google.common.util.concurrent.MoreExecutors;
27  
28  import java.util.List;
29  import java.util.concurrent.Callable;
30  import java.util.concurrent.Delayed;
31  import java.util.concurrent.ScheduledFuture;
32  import java.util.concurrent.TimeUnit;
33  
34  /**
35   * Factory methods for {@link ExecutorService} for testing.
36   *
37   * @author Chris Nokleberg
38   * @since 14.0
39   */
40  @Beta
41  public final class TestingExecutors {
42    private TestingExecutors() {}
43  
44    /**
45     * Returns a {@link ScheduledExecutorService} that never executes anything.
46     *
47     * <p>The {@code shutdownNow} method of the returned executor always returns an empty list despite
48     * the fact that everything is still technically awaiting execution.
49     * The {@code getDelay} method of any {@link ScheduledFuture} returned by the executor will always
50     * return the max long value instead of the time until the user-specified delay.
51     */
52    public static ListeningScheduledExecutorService noOpScheduledExecutor() {
53      return new NoOpScheduledExecutorService();
54    }
55  
56    /**
57     * Creates a scheduled executor service that runs each task in the thread
58     * that invokes {@code execute/submit/schedule}, as in
59     * {@link CallerRunsPolicy}. This applies both to individually submitted
60     * tasks and to collections of tasks submitted via {@code invokeAll},
61     * {@code invokeAny}, {@code schedule}, {@code scheduleAtFixedRate}, and
62     * {@code scheduleWithFixedDelay}.  In the case of tasks submitted by
63     * {@code invokeAll} or {@code invokeAny}, tasks will run serially on the
64     * calling thread.  Tasks are run to completion before a {@code Future} is
65     * returned to the caller (unless the executor has been shutdown).
66     *
67     * <p>The returned executor is backed by the executor returned by
68     * {@link MoreExecutors#newDirectExecutorService} and subject to the same
69     * constraints.
70     *
71     * <p>Although all tasks are immediately executed in the thread that
72     * submitted the task, this {@code ExecutorService} imposes a small
73     * locking overhead on each task submission in order to implement shutdown
74     * and termination behavior.
75     *
76     * <p>Because of the nature of single-thread execution, the methods
77     * {@code scheduleAtFixedRate} and {@code scheduleWithFixedDelay} are not
78     * supported by this class and will throw an UnsupportedOperationException.
79     *
80     * <p>The implementation deviates from the {@code ExecutorService}
81     * specification with regards to the {@code shutdownNow} method.  First,
82     * "best-effort" with regards to canceling running tasks is implemented
83     * as "no-effort".  No interrupts or other attempts are made to stop
84     * threads executing tasks.  Second, the returned list will always be empty,
85     * as any submitted task is considered to have started execution.
86     * This applies also to tasks given to {@code invokeAll} or {@code invokeAny}
87     * which are pending serial execution, even the subset of the tasks that
88     * have not yet started execution.  It is unclear from the
89     * {@code ExecutorService} specification if these should be included, and
90     * it's much easier to implement the interpretation that they not be.
91     * Finally, a call to {@code shutdown} or {@code shutdownNow} may result
92     * in concurrent calls to {@code invokeAll/invokeAny} throwing
93     * RejectedExecutionException, although a subset of the tasks may already
94     * have been executed.
95     *
96     * @since 15.0
97     */
98    public static SameThreadScheduledExecutorService sameThreadScheduledExecutor() {
99      return new SameThreadScheduledExecutorService();
100   }
101 
102   private static final class NoOpScheduledExecutorService
103       extends AbstractListeningExecutorService implements ListeningScheduledExecutorService {
104 
105     private volatile boolean shutdown;
106 
107     @Override public void shutdown() {
108       shutdown = true;
109     }
110 
111     @Override public List<Runnable> shutdownNow() {
112       shutdown();
113       return ImmutableList.of();
114     }
115 
116     @Override public boolean isShutdown() {
117       return shutdown;
118     }
119 
120     @Override public boolean isTerminated() {
121       return shutdown;
122     }
123 
124     @Override public boolean awaitTermination(long timeout, TimeUnit unit) {
125       return true;
126     }
127 
128     @Override public void execute(Runnable runnable) {}
129 
130     @Override public <V> ListenableScheduledFuture<V> schedule(
131         Callable<V> callable, long delay, TimeUnit unit) {
132       return NeverScheduledFuture.create();
133     }
134 
135     @Override public ListenableScheduledFuture<?> schedule(
136         Runnable command, long delay, TimeUnit unit) {
137       return NeverScheduledFuture.create();
138     }
139 
140     @Override public ListenableScheduledFuture<?> scheduleAtFixedRate(
141         Runnable command, long initialDelay, long period, TimeUnit unit) {
142       return NeverScheduledFuture.create();
143     }
144 
145     @Override public ListenableScheduledFuture<?> scheduleWithFixedDelay(
146         Runnable command, long initialDelay, long delay, TimeUnit unit) {
147       return NeverScheduledFuture.create();
148     }
149 
150     private static class NeverScheduledFuture<V>
151         extends AbstractFuture<V> implements ListenableScheduledFuture<V> {
152 
153       static <V> NeverScheduledFuture<V> create() {
154         return new NeverScheduledFuture<V>();
155       }
156 
157       @Override public long getDelay(TimeUnit unit) {
158         return Long.MAX_VALUE;
159       }
160 
161       @Override public int compareTo(Delayed other) {
162         return Longs.compare(getDelay(TimeUnit.NANOSECONDS), other.getDelay(TimeUnit.NANOSECONDS));
163       }
164     }
165   }
166 }